package org.yaac.client.channel;

import javax.inject.Inject;

import org.yaac.client.service.ChannelClientServiceAsync;
import org.yaac.client.widget.BlockingAsyncCallback;

import com.google.gwt.appengine.channel.client.Channel;
import com.google.gwt.appengine.channel.client.ChannelFactory;
import com.google.gwt.appengine.channel.client.ChannelFactory.ChannelCreatedCallback;
import com.google.gwt.appengine.channel.client.Socket;
import com.google.gwt.appengine.channel.client.SocketError;
import com.google.gwt.appengine.channel.client.SocketListener;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Window;

/**
 * used to subscribe contents from channel
 * 
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public class ChannelClientManager {

	public static interface Listener {
		void onMessage(String msg);
	}
	
	private final ChannelClientServiceAsync channelClientSerivce;
	
	/**
	 * there will be only one listener, as activity is not singleton
	 * multiple registration will cause same msg delivered for many times 
	 */
	private Listener listener;
	
	private Socket s;
	
	@Inject
	ChannelClientManager(ChannelClientServiceAsync channelClientSerivce) {
		super();
		this.channelClientSerivce = channelClientSerivce;
		
		// obtain channel token
		this.channelClientSerivce.requestChannelToken(new BlockingAsyncCallback<String>() {

			@Override
			public void onBlockingCallSuccess(String result) {
				openChannel(result);
			}

			@Override
			public void onBlockingCallFailure(Throwable caught) {
				Window.alert("Unable to open channel in server side. Please try to refresh your browser");
			}
		});
		
		// close channel on window close
		Window.addCloseHandler(new CloseHandler<Window>() {
			@Override
			public void onClose(CloseEvent<Window> event) {
				if (s != null) {
					s.close();	
				}
			}
		});
	}
	
	private void openChannel(String token) {
		BlockingAsyncCallback.block("Connecting to channel service");
		ChannelFactory.createChannel(token, new ChannelCreatedCallback() {
			@Override
			public void onChannelCreated(Channel channel) {
				s = channel.open(new SocketListener() {
					@Override
					public void onOpen() {
						BlockingAsyncCallback.unblock();
					}
					
					@Override
					public void onMessage(String message) {
						BlockingAsyncCallback.unblock();
						if (listener == null) {
							Window.alert("No listener is registered for server pushed message");
						} else {
							listener.onMessage(message);
						}
					}
					
					@Override
					public void onError(SocketError error) {
						BlockingAsyncCallback.unblock();
						
						// TODO : better way to handle this
						// TODO : reconnect when TOKEN-TIME-OUT
						Window.alert("ERROR : " + error.getDescription());
					}
					
					@Override
					public void onClose() {
						BlockingAsyncCallback.unblock();
						
						// TODO : better way to handle this
						Window.alert("Channel closed");
					}
				});
			}
		});	
	}
	
	public void subscribe(Listener listener) {
		this.listener = listener;
	}
}
